﻿/**
 * controlExtends.js jQuery plugins
 * @author shrdi_dev@yahoo.com.tw in Tainan Taiwan , started@2013.08.08.
 * 
 */
var ControlExtends = {};
ControlExtends.stopEvent = function(ev){
	ev.preventDefault(); // prevent elastic scrolling
	ev.stopPropagation(); // prevent event bubble up/down
	return false; // 寫這仍對於觸控原生捲動無作用, 但對滑鼠事件有用!
};

/** 請注意: touchend的event.ev.targetTouches沒有位置資料! */
ControlExtends.fingerPos = function(ev){
	var ans;
	if(ev.targetTouches && ev.targetTouches[0]){ // webkit browser
		ans = {};
		ans.x = ev.targetTouches[0].pageX; 
		ans.y = ev.targetTouches[0].pageY; 
	}
	return ans;
};

ControlExtends.mousePos = function(ev){
	var ans;
	if(ev.clientX && ev.clientY){ // webkit browser
		ans = {};
		ans.x = ev.clientX;
		ans.y = ev.clientY;
	}
	return ans;
};

ControlExtends.getPos = function(ev){
	var tmp, isTouch=ev.type.indexOf('touch')>=0;
	return isTouch ? ((tmp=ControlExtends.fingerPos(ev))?tmp:ControlExtends.mousePos(ev)) : ControlExtends.mousePos(ev);
};

ControlExtends.isPosInside = function(jqObj, pos){
	var ans = false, offset;
	try{
		offset = jqObj.offset();
		if((pos.x<=(offset.left+jqObj.width()) && pos.x>=offset.left)
			&& ((pos.y<=offset.top+jqObj.height()) && pos.y>=offset.top))
			ans = true;
	}catch(e){ }
	return ans;
};

ControlExtends.isFunction = function(functionToCheck){
	var obj = {};
	return functionToCheck && obj.toString.call(functionToCheck) === '[object Function]';
};

/** TODO : by device check合適的control mode! 主要在測試當touch event發生時, 會不會造成多個event間的干擾 */
ControlExtends.checkControlMode = function(){
	
};

ControlExtends.setTriggerDistForFlip = function(minDeltaX4HFlip, minDeltaY4VFlip){
	
};


ControlExtends.MODE_MOUSE = 77716801;
ControlExtends.MODE_TOUCH = 77716802;
ControlExtends.MODE_BOTH = 77716803;
ControlExtends._onTDownListeners = [];
ControlExtends._onTMoveListeners = [];
ControlExtends._onTUpListeners = [];
ControlExtends._onMDownListeners = [];
ControlExtends._onMMoveListeners = [];
ControlExtends._onMUpListeners = [];
ControlExtends._setEventListener = function(jqObj, evType, listener, mCEAttr, mCEEVListenerArr, mCEEVLstnrIdx){
	var isFunc=ControlExtends.isFunction(listener), isOK = false, domObj;
	if(evType.indexOf('touch') >= 0)
		try{ // html5 addEventListener for touch event
			jqObj.each(function(){
				domObj = $(this).get(0);
				if(mCEEVListenerArr[mCEEVLstnrIdx]){
					domObj.removeEventListener(evType, mCEEVListenerArr[mCEEVLstnrIdx]);
					mCEEVListenerArr[mCEEVLstnrIdx] = null;
				}
				if(isFunc){
					mCEEVListenerArr[mCEEVLstnrIdx] = listener;
					domObj.addEventListener(evType, mCEEVListenerArr[mCEEVLstnrIdx], false);
				}
				if(mCEEVListenerArr[mCEEVLstnrIdx])
					jqObj.attr(mCEAttr, mCEEVLstnrIdx);
				else
					jqObj.removeAttr(mCEAttr);
			});
			isOK = true;
		}catch(e){ }
	else if(evType.indexOf('mouse') >= 0){ // jQuery bind mouse event
		if(mCEEVListenerArr[mCEEVLstnrIdx]){
			jqObj.unbind(evType, mCEEVListenerArr[mCEEVLstnrIdx]);
			mCEEVListenerArr[mCEEVLstnrIdx] = null;
		}
		if(isFunc){
			mCEEVListenerArr[mCEEVLstnrIdx] = listener;
			jqObj.bind(evType, mCEEVListenerArr[mCEEVLstnrIdx]).attr(mCEAttr, mCEEVLstnrIdx);
		}
		if(!mCEEVListenerArr[mCEEVLstnrIdx])
			jqObj.removeAttr(mCEAttr);
		isOK = true;
	}
	// TODO : WP7 IE9Lite觸控需另外處理
	
	return isOK;
};

try{if(!$CE)$CE=ControlExtends;}catch(e){$CE=ControlExtends;}

try{if(jQuery);}catch(e){jQuery=null;}
if(jQuery)
(function($){
	/** 
	 * 1. listenser有設=取代前次設定, false=以$CE.stopEvent函式設入; null或其他非函數值=移除設定.
	 *    須注意有設listenser的話必須全權處理事件, 例如是否防止事件氣泡, 是否回傳處理結果(true/false).. 等,
	 *    可用$CE.getPos(), $CE.stopEvent().. 等public API簡化處理邏輯; 以底線_開頭的private api或屬性則不建議使用.
	 * 2. ceMode 有 $CE.MODE_MOUSE, $CE.MODE_TOUCH, $CE.MODE_BOTH 三種控制模式設定值, 沒給的話預設為$CE.MODE_BOTH .
	 */
	$.fn.down = function(listener, ceMode){
		var tdownAttr='shrdiTDownListenerIdx', mDownAttr='shrdiMDownListenerIdx', $CE=ControlExtends, isOK=false, idx;
		listener = listener==false ? $CE.stopEvent : ($CE.isFunction(listener)?listener:null);
		ceMode = ceMode ? ceMode : $CE.MODE_BOTH;
		if(ceMode==$CE.MODE_TOUCH || ceMode==$CE.MODE_BOTH)
			isOK = $CE._setEventListener(this, 'touchstart', listener, tdownAttr, $CE._onTDownListeners,
				((idx=this.attr(tdownAttr))&&!isNaN(idx) ? idx : $CE._onTDownListeners.length));
		// alert('is touchstart set?'+isOK);
		if(ceMode==$CE.MODE_MOUSE || ceMode==$CE.MODE_BOTH)
			isOK = $CE._setEventListener(this, 'mousedown', listener, mDownAttr, $CE._onMDownListeners,
				((idx=this.attr(mDownAttr))&&!isNaN(idx) ? idx : $CE._onMDownListeners.length));
		// alert('is mousedown set?'+isOK);
		return this;
	};

	/** 
	 * 1. listenser有設=取代前次設定, false=以$CE.stopEvent函式設入; null或其他非函數值=移除設定.
	 *    須注意有設listenser的話必須全權處理事件, 例如是否防止事件氣泡, 是否回傳處理結果(true/false).. 等,
	 *    可用$CE.getPos(), $CE.stopEvent().. 等public API簡化處理邏輯; 以底線_開頭的private api或屬性則不建議使用.
	 * 2. ceMode 有 $CE.MODE_MOUSE, $CE.MODE_TOUCH, $CE.MODE_BOTH 三種控制模式設定值, 沒給的話預設為$CE.MODE_BOTH .
	 */
	$.fn.move = function(listener, ceMode){
		var tmvAttr='shrdiTMoveListenerIdx', mmvAttr='shrdiMMoveListenerIdx', $CE=ControlExtends, isOK=false, idx;
		listener = listener==false ? $CE.stopEvent : ($CE.isFunction(listener)?listener:null);
		ceMode = ceMode ? ceMode : $CE.MODE_BOTH;
		if(ceMode==$CE.MODE_TOUCH || ceMode==$CE.MODE_BOTH)
			isOK = $CE._setEventListener(this, 'touchmove', listener, tmvAttr, $CE._onTMoveListeners, 
				((idx=this.attr(tmvAttr))&&!isNaN(idx) ? idx : $CE._onTMoveListeners.length));
		// alert('is touchmove set?'+isOK);
		if(ceMode==$CE.MODE_MOUSE || ceMode==$CE.MODE_BOTH)
			isOK = $CE._setEventListener(this, 'mousemove', listener, mmvAttr, $CE._onMMoveListeners, 
				((idx=this.attr(mmvAttr))&&!isNaN(idx) ? idx : $CE._onMMoveListeners.length));
		// alert('is mousemove set?'+isOK);
		return this;
	};
	
	/** 
	 * 1. listenser有設=取代前次設定, false=以$CE.stopEvent函式設入; null或其他非函數值=移除設定.
	 *    須注意有設listenser的話必須全權處理事件, 例如是否防止事件氣泡, 是否回傳處理結果(true/false).. 等,
	 *    可用$CE.getPos(), $CE.stopEvent().. 等public API簡化處理邏輯; 以底線_開頭的private api或屬性則不建議使用.
	 * 2. ceMode 有 $CE.MODE_MOUSE, $CE.MODE_TOUCH, $CE.MODE_BOTH 三種控制模式設定值, 沒給的話預設為$CE.MODE_BOTH .
	 */
	$.fn.up = function(listener, ceMode){
		var tupAttr='shrdiTUpListenerIdx', mupAttr='shrdiMUpListenerIdx', $CE=ControlExtends, isOK=false, idx;
		listener = listener==false ? $CE.stopEvent : ($CE.isFunction(listener)?listener:null);
		ceMode = ceMode ? ceMode : $CE.MODE_BOTH;
		if(ceMode==$CE.MODE_TOUCH || ceMode==$CE.MODE_BOTH)
			isOK = $CE._setEventListener(this, 'touchend', listener, tupAttr, $CE._onTUpListeners, 
				((idx=this.attr(tupAttr))&&!isNaN(idx) ? idx : $CE._onTUpListeners.length));
		// alert('is touchend set?'+isOK);
		if(ceMode==$CE.MODE_MOUSE || ceMode==$CE.MODE_BOTH)
			isOK = $CE._setEventListener(this, 'mouseup', listener, mupAttr, $CE._onMUpListeners, 
				((idx=this.attr(mupAttr))&&!isNaN(idx) ? idx : $CE._onMUpListeners.length));
		// alert('is mouseup set?'+isOK);
		return this;
	};
	
	/** 
	 * 1. listenser有設=取代前次設定, false=以$CE.stopEvent函式設入; null或其他非函數值=移除設定.
	 *    須注意有設listenser的話必須全權處理事件, 例如是否防止事件氣泡, 是否回傳處理結果(true/false).. 等,
	 *    可用$CE.getPos(), $CE.stopEvent().. 等public API簡化處理邏輯; 以底線_開頭的private api或屬性則不建議使用.
	 * 2. ceMode 有 $CE.MODE_MOUSE, $CE.MODE_TOUCH, $CE.MODE_BOTH 三種控制模式設定值, 沒給的話預設為$CE.MODE_BOTH .
	 */
	$.fn.act = function(downListener, moveListener, upListener, ceMode){
		var stopEVFun=ControlExtends.stopEvent, $CE=ControlExtends;
		downListener = downListener==false ? stopEVFun : ($CE.isFunction(downListener)?downListener:null);
		moveListener = moveListener==false ? stopEVFun : ($CE.isFunction(moveListener)?moveListener:null);
		upListener = upListener==false ? stopEVFun : ($CE.isFunction(upListener)?upListener:null);
		return this.down(downListener, ceMode).move(moveListener, ceMode).up(upListener, ceMode);
	};
	
	
	/** 
	 * ceMode 有 $CE.MODE_MOUSE, $CE.MODE_TOUCH, $CE.MODE_BOTH 三種控制模式設定值, 沒給的話預設為$CE.MODE_BOTH .
	 * 若已設過listener則覆蓋原有; 欲取消則給null或不設listener. listener程式末尾加return false可避免選取文字.
	 * 設 stopMoveWithTouchScroll = true 可避免行動裝置之瀏覽器頁面滑動(但會導致touchend的判別仍在同一個元件上!!). 
	 * stopMoveWithTouchScroll 可給 function, 讓developer自行處理 touchmove事件!
	 * TODO : upInsideListener 若有給function, 則當user在物件範圍內up時回呼此callback
	 */
	$.fn.press = function(ceMode, listener, stopMoveWithTouchScroll, moveOutsideListener, upInsideListenerm, upOutsideListener){
		// TODO : for 觸控裝置, long press或 touchmove 皆會取消 click事件!
		// => 好的體驗需在"一定範圍"內的觸控doen->up, 自動幫忙觸發(trigger) click事件 (否則就是"感覺難按")
		// 	==>> 1. up時計算dx&dy判斷移動範圍是否在物件寬高內
		// => 如何判斷是否是系統真正觸發 click事件或 trigger之? 或把真正的click事件取消掉自己處理?
		
		//this._mouseTool.set(this, this._evHelper);
		//this._fingerTool.set(this, this._evHelper);
		
		this.down(listener, ceMode);
		
		var tmvAttr='shrdiTMoveListenerIdx', tdownmvListener, idx, stopFunc=stopMoveWithTouchScroll;
		tdownmvListener = stopFunc ? ($CE.isFunction(stopFunc)?stopFunc:$CE.stopEvent) : null;
		$CE._setEventListener(this, 'touchmove', tdownmvListener, tmvAttr, $CE._onTMoveListeners, 
			((idx=this.attr(tmvAttr))&&!isNaN(idx) ? idx : $CE._onTMoveListeners.length));
		
		/* TODO
		if(upInsideListener){
			this._upInsideCallback = upInsideListener;
			this.up(ceMode, function(ev){
				
			});
		}
		*/
			
		return this;
	};
	
	/** Smart button click event callback api, it will reset the 'down'-eventListener, 'move'-eventListener, and 'up'-eventListener. This might substitue and let real 'click' event disable. 
	 1. callback is one of the 'up'-eventListener's action, just while 'up'-event occurrs inside of the button, the callback will be call, and send the 'up'-event obj back. of cource you can still use 'this' in your callback to operate this dom.
	 2. ceMode could be one of $CE.MODE_MOUSE, $CE.MODE_TOUCH, $CE.MODE_BOTH; otherwise, default is $CE.MODE_BOTH.
	 3. onDownCSSClass is optional. if given, the dom takes this class on 'down'-event occurrs and forsakes on 'up'-event occurs.
	 4. preventMoveEventBubble : Only while it be true or function, the dom-obj's 'move'-eventListener will be set. if true, the 'move'-eventListener is $CE.stopEvent; if a function, the 'move'-eventListener is defined by yourself.
	 */
	$.fn.btnClick = function(callback, ceMode, onDownCSSClass, preventMoveEventBubble, isCallbackWhileOffsetChanged, isRemoveDowncssOnMoveOutside){
		
	};
})(jQuery);